#pragma once
#ifndef _FEM_FRONTAL_TRIANGULATION_
#define _FEM_FRONTAL_TRIANGULATION_

//================================================================================================================================
//================================================================================================================================
// QtCore
#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QVector>
#include <QtCore/QStringList>
// Локальные
#include "geometry/2d/point.h"
#include "fem/subdomain.h"
#include "fem/trianglenet.h"
#include "fem/bordersegment.h"
#include "fem/triangleelement.h"
#include "core_global.h"

//================================================================================================================================
//================================================================================================================================
namespace fem
{
    /// Класс, реализующий триангуляцию расчетной области фронтальным методом.
    //
    /// Перед тем как выполнять триангуляцию области, необходимо задать "скелет" триангуляции(разбить расчетную область на подобласти и
    /// задать характеристики этих подобластей). Делается это в три этапа:\n
    /// 1 - Задаются опорные точки. Это точки, лежащие внутри или на границе расчетной области. Впоследствии они станут вершинами
    /// n-угольных подобластей.\n
    /// 2 - Опорные точки соединяются сегментами так, чтобы расчетная область была разбита на непересекающиеся подобласти.\n
    /// 3 - Явно указываются подобласти расчетной области.
    ///
    /// Класс допускает возможность того, что на одной сетке могут считаться несколько функций. Информация о краевых условиях хранится
    /// отдельно от информации о сетке. Для каждой функции это список "троек" 'индекс сегмента'-'тип КУ'-'индекс формулы для расчета КУ'.
    /// Доступ к информации осуществляется посредством текстовых уникалбных идентификаторов функций.
    /// \sa Метод конечных элементов для решения скалярных и векторных задач : учеб. пособие / Ю.Г. Соловейчик, М.Э. Рояк,
    /// М.Г. Персова - Новосибирск: Изд-во НГТУ, 2007. - С. 474-480.
    /// \sa geometry::two_d::Point2D
    /// \sa Subdomain
    /// \sa BorderSegment
    /// \sa TriangleElement
    /// \sa TriangleNet
    class CORE_EXPORT FrontalTriangulation
    {
    public:
        /// \param f_ids Текстовые идентификаторы функций, значения которых ищутся на данной сетке.
        explicit FrontalTriangulation(const QStringList &f_ids);

        /// Добавление опорной точки.
        //
        /// Впоследствии опорные точки будут служить вершинами сегментам границ расчетной области.
        /// \return Индекс добавленной точки.
        int addBasisPoint(const geometry::_2d::Point &point)
        {
            m_Points.append(point);
            return m_Points.size() - 1;
        }

        int pointsCount(int segment){return m_BorderSegments[segment].numOfPoints();}

        /// Добавление сегмента границ расчетной области.
        /// \return Индекс добавленного сегмента.
        int addBorderSegment(const BorderSegment &segment)
        {
            m_BorderSegments.append(segment);
            return m_BorderSegments.size() - 1;
        }

        /// Создание сегмента границ расчетной области.
        /// \param begin Индекс опорной точки, являющейся началом сегмента.
        /// \param end Индекс опорной точки, являющейся концом сегмента.
        /// \param num_of_knots Число точек расчетной области, которые будут расположены вдоль сегмента.
        /// \return Индекс добавленного сегмента.
        /// \remarks \e begin и \e end - индексы существующих опорных точек.
        /// \sa addBasisPoint().
        int addBorderSegment(int begin, int end, int num_of_knots);

        /// Создание сегмента границ расчетной области.
        /// \param begin Индекс опорной точки, являющейся началом сегмента.
        /// \param end Индекс опорной точки, являющейся концом сегмента.
        /// \param num_of_knots Число точек расчетной области, которые будут расположены вдоль сегмента.
        /// \param spacing_factor Коэффициент разрядки.
        /// \return Индекс добавленного сегмента.
        /// \remarks \e begin и \e end - индексы существующих опорных точек.
        /// \sa addBasisPoint().
        int addBorderSegment(int begin, int end, int num_of_knots, double spacing_factor);

        /// Добавление дополнительной информации о краевых условиях на сегменте границы расчетной области.
        //
        /// Для типа краевого условия допустимы следующие значения:\n
        /// 1 - Краевое условие Дирихле.\n
        /// 2 - Краевое условие Неймана.\n
        /// 3 - Краевое условие Коши.\n
        /// 4 - Условие идеального контакта.\n
        /// Для условий идеального контакта параметр \e bc_formula игнорируется. При создании границ с вышеназванными краевыми
        /// условиями для параметра \e bc_formula можно оставить значение по умолчанию. При использовании значения по умолчания
        /// для КУ другого типа будет выводиться предупреждение.
        /// \param f_id Индекс функции, для которой задается информация о КУ.
        /// \param segment Индекс сегмента границы расчетной области, для которого добавляется информация.
        /// \param bc_type Тип краевого условия ассоциированного с сегментом.
        /// \param bc_formula Номер формулы по которой расчитывается значение краевого условия.
        /// \sa addBasisPoint().
        void addBorderSegmentBCInfo(const QString &f_id, int segment, int bc_type, int bc_formula = -1);

        /// Объединение двух сегментов.
        //
        /// Функция создает новый сегмент, получающийся добавлением сегмента с индексом \e b к концу сегмента с индексом \e a.
        /// \return Индекс нового сегмента, являющегося объединением двух старых.
        int concatinateBorderSegments(int segment1, int segment2);

        /// Создание псевдотреугольной подобласти расчётной области, окруженной границей из сегментов \e b_segment1, \e b_segment2 и
        /// \e b_segment3.
        /// \return Индекс созданной подобласти.
        int addTriangleSubdomain(int b_segment1, int b_segment2, int b_segment3, int material)
        {
            QList<int> border_segments;
            border_segments.append(b_segment1);
            border_segments.append(b_segment2);
            border_segments.append(b_segment3);

            m_TriangleSubdomains.append(Subdomain(border_segments, material));
            return m_TriangleSubdomains.size() - 1;
        }

        /// Создание псевдочетырехугольной подобласти расчётной области, окруженной границей из сегментов \e b_segment1, \e b_segment2,
        /// \e b_segment3 и \e b_segment4.
        /// \return Индекс созданной подобласти.
        int addQuadrangleSubdomain(int b_segment1, int b_segment2, int b_segment3, int b_segment4, int material)
        {
            QList<int> border_segments;
            border_segments.append(b_segment1);
            border_segments.append(b_segment2);
            border_segments.append(b_segment3);
            border_segments.append(b_segment4);

            m_QuadrangleSubdomains.append(Subdomain(border_segments, material));
            return m_QuadrangleSubdomains.size() - 1;
        }

        /// Создание конечноэлементной сетки.
        TriangleNet triangulate();

        void getSegmentPoints(int segment, QList<double> &x, QList<double> &y)
        {
            x.clear();
            y.clear();

            for(int i = 0; i < m_BorderSegments[segment].numOfPoints(); ++i)
            {
                x.append(m_Points[m_BorderSegments[segment].point(i)].x());
                y.append(m_Points[m_BorderSegments[segment].point(i)].y());
            }
        }

        /// Получение информации об участках границы расчетной области с заданными на них первыми краевыми условиями.
        /// \param f_id Идентификатор функции, для которой нужно получить информацию о краевых условиях.
        /// \param[out] points Номера узлов, в которых задано краевой условие первого рода.
        /// \param[out] conditions Номера формул по которым вычисляются значения краевого условия в точках.
        void getDirichletBoundaryInfo(const QString &f_id, QList<int> &points, QList<int> &conditions) const;

    private:
        /// Предобработка треугольной подобласти.
        //
        /// Информация структурируется таким образом, чтобы минимизировать число условных операций при триангуляции. Для этого, по
        /// необходимости, меняется локальная нумерация сегментов, ограничивающих подобласть и/или осуществляется инвертирование сегментов:\n
        /// 1)Если есть два сегмента с одинаковым числом точек, то они переименовываются в сегменты a и b;\n
        /// 2)Начала сегментов a и b совпадают;\n
        /// 3)Начало сегмента c совпадает с концом b, а конец сегмента c - с концом a.\n
        void prepareTriangleSubdomain(int subdomain);

        /// Предобработка четырехугольной подобласти.
        //
        /// Информация структурируется таким образом, чтобы минимизировать число условных операций при триангуляции. Для этого, по
        /// необходимости, меняется локальная нумерация сегментов, ограничивающих подобласть и/или осуществляется инвертирование сегментов:\n
        /// 1)Если есть противолежащие сегменты с одинаковым числом точек, то они переименовываются в a и c\n
        /// 2)Сегменты a и c ориентируются от d к b(т.е. их начала совпадают с конечными точками сегмента d, а их концы - с конечными точками сегмента b)\n
        /// 3)Сегменты b и d ориентируются от c к a(т.е. их начала совпадают с конечными точками сегмента c, а их концы - с конечными точками сегмента a)\n
        void prepareQuadrangleSubdomain(int subdomain);

        /// Триангуляция псевдотреугольной подобласти
        void triangulateTriangleSubdomain(int subdomain);

        /// Триангуляция псевдочетырехугольной подобласти
        void triangulateQuadrangleSubdomain(int subdomain);

        /// Информация о краевых условиях для сегмента границы подобласти расчетной области.
        struct BorderSegmentBCInfo
        {
            /// Индекс сегмента границы подобласти расчетной области.
            int Segment;

            /// Тип краевого условия.
            //
            /// Для типа краевого условия допустимы следующие значения:\n
            /// 1 - Краевое условие Дирихле.\n
            /// 2 - Краевое условие Неймана.\n
            /// 3 - Краевое условие Коши.\n
            /// 4 - Условие идеального контакта.
            int BCType;

            /// Номер формулы, по которой вычисляется значение КУ.
            //
            /// Для условий идеального контакта (BCType = 4) параметр BCFormula игнорируется.
            int BCFormula;
        };
        /// Список сегментов границы с заданными на них КУ для каждой функции.
        QVector< QList<BorderSegmentBCInfo> > m_BorderSegmentsBCInfo;
        /// Таблица сопоставления текстового идентификатора функции числовому.
        QHash<QString, int> m_FunctionsIDTable;

        QList<geometry::_2d::Point> m_Points;       ///< Набор точек расчетной области, служащих выршинами конечных элементов.
        QList<BorderSegment> m_BorderSegments;      ///< Набор сегментов, разбивающих расчетную область на подобласти.
        QList<Subdomain> m_TriangleSubdomains;      ///< Набор псевдотреугольных подобластей расчетной области.
        QList<Subdomain> m_QuadrangleSubdomains;    ///< Набор псевдочетырехугольных областей расчетной области.
        QList<TriangleElement> m_TriangleElements;  ///< Набор треугольных конечных элементов.
    };
}

#endif // _FEM_FRONTAL_TRIANGULATION_
